/**
@author		Jong Min, Lee
@date		  01/15/2014
@breif		MonitorAdmin class

copyright(c) Tae Young Soft. All right reserved. 2014
*/
#pragma once

#include <list>
#include <vector>
#include <algorithm>

#include "Taurus_Util/Macros.h"
#include "Taurus_Common/TCDefine.h"

//forward declarations
class CMonitor;
class IViewerProperty;

class TAURUS_COMMON_EXPORT CMonitorAdmin
{
private:
  /**
  @brief		default constructor. this class has only the single instance
  */
  CMonitorAdmin();

  DISALLOW_COPY_AND_ASSIGN(CMonitorAdmin);

public:
  /**
  @brief		destrcutor
  */
  ~CMonitorAdmin();

  /**
  @brief		initialize monitor admin. this method gather information of monitors that be installed on system
  @return		TRUE if success, FALSE otherwise
  */
  BOOL init();

  /**
  @brief		configure monitor properties
  @param		pProperties pointer to viewer properties
  @return		ERROR_SUCCESS upon success, an error otheriwse
  */
  DWORD doConfigure(IViewerProperty* pProperty);

  /**
  @brief		get the single instance of monitor admin
  @return		pointer to the single instance of monitor admin
  */
  static CMonitorAdmin* getInstance(void);

  /**
  @brief		access to a monitor by using index number
  @param    nIndex index number of monitor
  @return		pointer to monitor if found, NULL if not
  */
  const CMonitor* GetMonitor(unsigned nIndex) const;

  /**
  @brief		get the number of monitors that be installed on system
  @return		the number of monitors that be installed on system
  */
  inline unsigned getNumMonitors() const
  {
    return m_listMonitors.size();
  }

  /**
  @brief		return a first monitor for which pred returns true
          	if no such monitor is found, this method return NULL
  @param    pred unary function that accepts pointer to monitor
  @return		pointer to monitor
  */
  template<class UnaryPredicate>
  inline const CMonitor* findMonitorIf(UnaryPredicate pred) const
  {
    auto res = std::find_if(m_listMonitors.cbegin(), m_listMonitors.cend(), pred);
    if(res != m_listMonitors.cend())
      return (*res);
    return NULL;
  }

  /**
  @brief		applies function fn to each of monitors
  @param		fn Unary function that accepts pointer to const monitor as argument
  */
  template<class Function>
  inline void forEachMonitor(Function fn) const
  {
    auto first = m_listMonitors.cbegin();
    auto last = m_listMonitors.cend();
    while(first != last)
    {
      fn((const CMonitor*)(*first));
      ++first;
    }
  }

  /**
  @brief		access to an visile monitor by index number
  @param		nIndex index number of visible monitor
  @return		pointer to monitor if found, NULL if not
  */
  const CMonitor* getVisibleMonitor(unsigned nIndex) const;

  /**
  @brief		get the number of visibile monitors
  @return		the number of visible monitors
  */
  inline unsigned getNumVisibleMonitors() const
  {
    return m_listVisibleMonitors.size();
  }

  /**
  @brief applies function fn to each of visible monitors
  @param fn Unary function that accepts pointer to const monitor as argument.
         if fn return false, this method stop calling fn
  */
  template<class Function>
  inline void forEachVisibleMonitors(Function fn) const
  {
    auto first = m_listVisibleMonitors.cbegin();
    auto last = m_listVisibleMonitors.cend();
    while(first != last)
    {
      if(!fn((const CMonitor*)(*first)))
        break;
      ++first;
    }
  }

  /**
  @brief		access to a hidden monitor
  @param		nIndex index number of hidden monitor
  @return		pointer to monitor if found, NULL if not
  */
  const CMonitor* getHiddenMonitor(unsigned nIndex) const;

  /**
  @brief		get the number of hidden monitors
  @return		the number of idden monitors
  */
  inline unsigned getNumHiddenMonitors() const
  {
    return m_listHiddenMonitors.size();
  }

  /**
  @brief applies function fn to each of visible monitors
  @param fn Unary function that accepts pointer to const monitor as argument.
  */
  template<class Function>
  inline void forEachHiddenMonitors(Function fn) const
  {
    auto first = m_listVisibleMonitors.cbegin();
    auto last = m_listVisibleMonitors.cend();
    while(first != last)
    {
      if(!fn((const CMonitor*)(*first)))
        break;
      ++first;
    }
  }

  /**
  @brief		get pointer to initial worklist monitor
            this function must be called after calling LoadProfile function
  @return		pointer to monitor
  */
  inline const CMonitor* getInitWorklistMonitor() const
  {
    return m_pInitWorklistMonitor;
  }

  /**
  @brief		access a monitor layout string
  @param		index the index number of monitor layout string
  @return		pointer to monitor layout string if found, nullptr if not
  */
  const TCHAR* getMonitorLayoutString(unsigned index) const;

  /**
  @brief		get the number of monitor layout strings
  @return		the number of monitor layout string
  */
  inline unsigned getNumMonitorLayoutStrings() const
  {
    return m_arrMonitorLayoutString.size();
  }

private:
  
  /**
  @brief		add a monitor to monitor list
  @param		pMonitor pointer to monitor to added
  @return		ERROR_SUCCESS upon success, an error code otherwise
  */
  DWORD addMonitor(CMonitor* pMonitor);

  /**
  @brief		remove all monitors
  */
  void removeAllMonitors();

  /**
  @brief		callback function that be used when gathering monitor information
  */
  static BOOL CALLBACK monitorEnumProc(HMONITOR hMonitor,
                                       HDC hDC,
                                       LPRECT lpRect,
                                       LPARAM lParam);

  /**
  @brief		build monitor layout strings
  @return		ERROR_SUCCESS upon success, an error code otherwise
  */
  DWORD buildMonitorLayoutString();

  /**
  @brief		clear monitor properties
  */
  void clearProperty();

private:
  //pointer to the single instance of monitor admin
  static CMonitorAdmin* m_pInstace;

  //monitors that be installed on system
  std::list<CMonitor*> m_listMonitors;

  //visible monitors
  std::list<CMonitor*> m_listVisibleMonitors;

  //hidden monitors
  std::list<CMonitor*> m_listHiddenMonitors;

  //pointer to intial worklist monitor
  CMonitor* m_pInitWorklistMonitor;

  //monitor layout strings
  std::vector<CString> m_arrMonitorLayoutString;
};